/*
 * world_boss->cpp
 *
 *  Created on: Feb 11, 2014
 *      Author: killerg
 */

#include "../server_deps.h"
#include "../server.h"
#include "../server_cfg.h"

#include "world_boss.h"
#include "game_cfg.h"
#include "player_data.h"
#include "mail_mgr.h"
#include "chat_mgr.h"
#include "player_mgr.h"

#include "share/msg.h"
#include "share/util.h"
#include "share/db_mgr.h"
#include "share/clock.h"

int32_t world_boss_t::get_max_hp() {
	auto pet = boss->pets.get(boss->combat[0]);
	if (!pet)
		return 0;

	return pet->att(cs::Pe4s_Att1);
}

bool world_boss_t::_gen_boss() {
	if (!boss)
		boss = make_shared<level_t>();
	else
		boss->destroy();

	assert(boss_levelid >= 0);

	if (!boss->init_worldboss(boss_levelid, level)) {
		boss->destroy();
		boss.reset();
		cur_hp = 0;
		return false;
	}

	cur_hp = get_max_hp();

	return true;
}


bool world_boss_t::init() {
	if (!g_dbf->ok())
		return false;

	bool res = g_dbf->get(
			fmt_cstr("_world_boss_data:%d", g_cfg->ServerId),
			[&res, this](db_error_t err, const Json::Value &json){
				switch (err) {
				case DBE_Success:
				{
					data.clear();
					rank.clear();
					last_attack = json["last_attack"].asInt64();
					const Json::Value &data_json = json["data_json"];
					for (auto itr = data_json.begin(); itr != data_json.end(); ++itr) {
						auto rank_item = make_shared<rank_item_t>();
						rank_item->name = (*itr)["name"].asCString();
						rank_item->playerid = (*itr)["playerid"].asInt64();
						rank_item->damage = (*itr)["damage"].asInt();
						rank_item->rank_damage = (*itr)["rank_damage"].asInt();
						rank_item->combat_time = (*itr)["combat_time"].asInt();
						rank_item->rank = (*itr)["rank"].asInt();

						data[rank_item->playerid] = rank_item;
						if (rank_item->rank > 0)
							rank.push_back(rank_item);
					}
					res = true;
				}
					break;
				case DBE_NoExist:
					res = true;
					last_attack = -1;
					data.clear();
					rank.clear();
					break;
				default:
					res = false;
					break;
				}
			});
	g_dbf->wait();
	if (!res) {
		UTIL_LOG(E, "err post load world_boss data");
		return false;
	}

	res = g_dbf->get(
			fmt_cstr("_world_boss_status:%d", g_cfg->ServerId),
			[&res, this](db_error_t err, const Json::Value &json){
				switch (err) {
				case DBE_Success:
				{
					boss_levelid = json.get("boss_levelid", -1).asInt();
					if (boss_levelid < 0) {
						boss_levelid = g_game_cfg->WorldBossRandom[random(0,2)];
					}
					status = (state_t)json.get("status", -1).asInt();
					finish = json.get("finish", -1).asInt();
					reward_index = json.get("reward_index", -1).asInt();
					level = json.get("level", 20).asInt();
					bool ok = _gen_boss();
					assert(ok);
					cur_hp = json["cur_hp"].asInt();

					res = true;
				}
					break;
				case DBE_NoExist:
				{
					boss_levelid = g_game_cfg->WorldBossRandom[random(0,2)];
					status = RESTING;
					finish = -1;
					reward_index = -1;
					level = 20;
					bool ok = _gen_boss();
					assert(ok);

					res = true;
				}
					break;
				default:
					res = false;
					break;
				}
			});
	g_dbf->wait();
	if (!res) {
		UTIL_LOG(E, "err post load world_boss data");
		return false;
	}

	return true;
}
void world_boss_t::destroy() {
	data.clear();
	rank.clear();
	if (boss) {
		boss->destroy();
		boss.reset();
	}
}
void world_boss_t::update() {
	switch (status) {
	case RESTING:
		{
			if (!turnon && g_clock->min_jump) {
				for (int i=0; i<2; ++i)
					if (g_game_cfg->WorldBossStartHour[i] == g_clock->tm_now.tm_hour &&
						g_game_cfg->WorldBossStartMin[i] == g_clock->tm_now.tm_min) {
						turnon = true;
						break;
					}
			}

			if (turnon) {
				turnon = false;

				status = COMBATING;
				finish = g_clock->tt_now + g_game_cfg->WorldBossLastMin * 60;

				last_attack = -1;
				data.clear();
				rank.clear();

				_post_save_data();
				_post_save_status();

				CREATE_CS_SMSG(SActivityState, activitystate);
				activitystate->set_activity(SActivityState::WorldBoss);
				activitystate->set_stage(SActivityState::Start);
				g_chat->broad_msg(chatmgr_t::sp_msg_t(___msg));
			}
		}
		break;
	case COMBATING:
		{
			if (g_clock->tt_now >= finish || cur_hp <= 0) {
				status = REWARDING;
				reward_index = 0;
				_post_save_status();

				CREATE_CS_SMSG(SActivityState, activitystate);
				activitystate->set_activity(SActivityState::WorldBoss);
				activitystate->set_stage(SActivityState::Finish);
				g_chat->broad_msg(chatmgr_t::sp_msg_t(___msg));
			}


			rank_timer -= g_clock->delta;
			if ((rank_chg && rank_timer < 0) || status == REWARDING) {
				rank_chg = false;
				rank_timer = 10.0f;

				rank.clear();

				for (const auto &itr : data) {
					rank.push_back(itr.second);
					itr.second->rank_damage = itr.second->damage;
				}

				sort(rank.begin(), rank.end(), comp());
				for (int i=0; i<rank.size(); ++i) {
					rank[i]->rank = i+1;
					if (status == REWARDING)
						rank[i]->combat_time = 0;
				}

				_post_save_data();
			}
		}
		break;
	case REWARDING:
		{
			if (reward_index >= rank.size()) {
				if (last_attack >= 0 && g_game_cfg->WorldBossLastAttackReward >= 0) {
					g_mail->send_system(
							last_attack,
							EMT_WorldBossReward,
							"",
							g_game_cfg->WorldBossLastAttackReward,
							1.0f,
							[this](bool ok){
								if (!ok) {
									UTIL_LOG(E,
											"error reward worldboss playerid:%lld rewardid:%d",
											last_attack,
											g_game_cfg->WorldBossLastAttackReward);
								}
							});
				}

				status = RESTING;
				reward_index = -1;
				finish = -1;
				boss_levelid = g_game_cfg->WorldBossRandom[random(0, 2)];
				if (cur_hp <= 0 && level < 50)
					++level;
				_gen_boss();

				_post_save_status();
				break;
			}

			// reward
			auto cur_index = reward_index++;
			auto data = rank[cur_index];
			assert(data);
            int32_t rewardid = -1;
            int32_t cur_rank = cur_index + 1;
            for (auto &itr : g_protomgr->_arrProtoWorldBossReward) {
                if (cur_rank >= itr.min && cur_rank <= itr.max) {
                    rewardid = itr.rewardid;
                    break;
                }
            }

            if (cur_index < 8) {
                g_playermgr->player_load(data->playerid, [](sp_player_t player){
                	if (!player)
                		return;
                	player->att(Pl4s_WeeklyCnt6WorldBoss, player->att(Pl4s_WeeklyCnt6WorldBoss) + 1);
                });
            }

            if (rewardid >= 0)
				g_mail->send_system(
						data->playerid,
						EMT_WorldBossReward,
						"",
						rewardid,
						1.0f,
						[data, rewardid](bool ok){
							if (!ok) {
								UTIL_LOG(E,
										"error reward worldboss playerid:%lld rewardid:%d",
										data->playerid,
										rewardid);
							}
						});

			_post_save_status();
		}
		break;
	}
}

void world_boss_t::_post_save_data() {
	if (!g_dbf->ok())
		return;

	Json::Value json;
	json["last_attack"] = Json::Int64(last_attack);
	Json::Value &data_json = json["data"];
	Json::Value one_data_json;
	for (const auto &itr : rank) {
		one_data_json["name"] = itr->name;
		one_data_json["playerid"] = Json::Int64(itr->playerid);
		one_data_json["damage"] = itr->damage;
		one_data_json["rank_damage"] = itr->rank_damage;
		one_data_json["combat_time"] = itr->combat_time;
		one_data_json["rank"] = itr->rank;

		data_json.append(one_data_json);
	}

	g_dbf->set(
			fmt_cstr("_world_boss_data:%d", g_cfg->ServerId),
			json, [](db_error_t err){
				switch (err) {
				case DBE_Success:
					break;
				default:
					UTIL_LOG(E, "err save world_boss status %s", db_strerror(err));
					break;
				}
			});

}
void world_boss_t::_post_save_status() {
	if (!g_dbf->ok())
		return;

	Json::Value json;
	json["boss_levelid"] = boss_levelid;
	json["cur_hp"] = cur_hp;
	json["status"] = status;
	json["finish"] = finish;
	json["reward_index"] = reward_index;
	json["level"] = level;

	g_dbf->set(
			fmt_cstr("_world_boss_status:%d", g_cfg->ServerId),
			json, [](db_error_t err){
				switch (err) {
				case DBE_Success:
					break;
				default:
					UTIL_LOG(E, "err save world_boss status %s", db_strerror(err));
					break;
				}
			});
}
